using System;
using System.Collections;
using System.IO;
using System.Net.Sockets;
using System.Security;
using OracleInternal.Common;

namespace OracleInternal.Network
{
	internal class OracleCommunication
	{
		internal delegate void InbandNotification(int InbandErr1, int InbandErr2, int InbandEMFI, string InbandErrMsg, object InbandErrContext);

		internal InbandNotification m_inbandNotification;

		internal object m_InbandErrContext;

		private ConnectionOption m_connOption;

		internal SessionContext m_sessionCtx;

		private bool m_lsnEP;

		private bool m_cltEP;

		private bool m_NSHandshakeComplete;

		internal Ano m_anoObject;

		private bool m_NAHandshake;

		private string m_InstanceName;

		private ConOraBufPool m_oraBufPool;

		private Hashtable m_ObCfgHT;

		private Hashtable m_ObTnsHT;

		private Hashtable m_ObLdpHT;

		private string m_ObCfgWL;

		private string m_ServerIs;

		private string m_ShardingKeyB64;

		private string m_SuperShardingKeyB64;

		private bool m_RAW;

		internal bool m_KeepAlive;

		internal int m_KeepAliveTime;

		internal int m_KeepAliveInterval;

		private SqlNetOraConfig SNOConfig;

		private DataPacket m_DataPacket;

		internal Socket Socket
		{
			get
			{
				if (m_sessionCtx == null)
				{
					return null;
				}
				return m_sessionCtx.m_socket;
			}
		}

		internal Stream SocketStream
		{
			get
			{
				if (m_sessionCtx == null)
				{
					return null;
				}
				return m_sessionCtx.m_socketStream;
			}
		}

		internal bool RAW
		{
			get
			{
				return m_RAW;
			}
			set
			{
				m_RAW = value;
			}
		}

		internal string ServerIs
		{
			get
			{
				return m_ServerIs;
			}
			set
			{
				m_ServerIs = value;
			}
		}

		internal string ShardingKeyB64
		{
			get
			{
				return m_ShardingKeyB64;
			}
			set
			{
				m_ShardingKeyB64 = value;
			}
		}

		internal string SuperShardingKeyB64
		{
			get
			{
				return m_SuperShardingKeyB64;
			}
			set
			{
				m_SuperShardingKeyB64 = value;
			}
		}

		internal string ObCfgWL
		{
			get
			{
				return m_ObCfgWL;
			}
			set
			{
				m_ObCfgWL = value;
			}
		}

		internal Hashtable ObCfgHT
		{
			get
			{
				return m_ObCfgHT;
			}
			set
			{
				m_ObCfgHT = value;
			}
		}

		internal Hashtable ObTnsHT
		{
			get
			{
				return m_ObTnsHT;
			}
			set
			{
				m_ObTnsHT = value;
			}
		}

		internal Hashtable ObLdpHT
		{
			get
			{
				return m_ObLdpHT;
			}
			set
			{
				m_ObLdpHT = value;
			}
		}

		internal bool KeepAlive
		{
			get
			{
				return m_KeepAlive;
			}
			set
			{
				m_KeepAlive = value;
			}
		}

		internal int KeepAliveTime
		{
			get
			{
				return m_KeepAliveTime;
			}
			set
			{
				m_KeepAliveTime = value;
			}
		}

		internal int KeepAliveInterval
		{
			get
			{
				return m_KeepAliveInterval;
			}
			set
			{
				m_KeepAliveInterval = value;
			}
		}

		internal InbandNotification InbandNotifier
		{
			get
			{
				return m_inbandNotification;
			}
			set
			{
				m_inbandNotification = value;
			}
		}

		internal object InbandNotifContext
		{
			get
			{
				return m_InbandErrContext;
			}
			set
			{
				m_InbandErrContext = value;
			}
		}

		internal bool READONLY_CHUNK_OK
		{
			get
			{
				if (m_connOption == null)
				{
					return false;
				}
				return m_connOption.READONLY_CHUNK_OK;
			}
		}

		internal string ConnectDescriptor
		{
			get
			{
				if (m_sessionCtx.isNTConnected)
				{
					return m_sessionCtx.m_connectData;
				}
				return null;
			}
		}

		internal ConOraBufPool OraBufPool
		{
			get
			{
				return m_oraBufPool;
			}
			set
			{
				m_oraBufPool = value;
				m_sessionCtx.m_transportAdapter.OraBufPool = value;
			}
		}

		internal ConnectionOption ConnectionOption => m_connOption;

		internal int SDU => m_sessionCtx.m_sessionDataUnit;

		internal string Server
		{
			get
			{
				if (m_connOption == null)
				{
					return null;
				}
				return m_connOption.Server;
			}
		}

		internal bool TransportAlive
		{
			get
			{
				bool flag = false;
				if (m_sessionCtx == null)
				{
					return false;
				}
				try
				{
					if (m_sessionCtx.m_gotInBandNotif || !m_sessionCtx.isNTConnected || m_sessionCtx.m_transportAdapter == null || !m_sessionCtx.m_transportAdapter.Connected || (m_sessionCtx.m_readerStream != null && m_sessionCtx.m_readerStream.m_EOF))
					{
						flag = false;
						return false;
					}
					if (m_DataPacket == null)
					{
						m_DataPacket = new DataPacket(m_sessionCtx, Packet.NSPOVR_SZ);
					}
					Socket testSocket = m_sessionCtx.m_testSocket;
					if (testSocket != null)
					{
						if (testSocket.Poll(0, SelectMode.SelectWrite))
						{
							m_DataPacket.Send(DataPacket.NSPDAFZER);
						}
						if (testSocket.Poll(0, SelectMode.SelectRead))
						{
							m_sessionCtx.m_readerStream.CheckInBandNotif_Migration();
						}
					}
					if (m_sessionCtx.m_gotInBandNotif || !m_sessionCtx.isNTConnected || m_sessionCtx.m_transportAdapter == null || !m_sessionCtx.m_transportAdapter.Connected || (m_sessionCtx.m_readerStream != null && m_sessionCtx.m_readerStream.m_EOF))
					{
						flag = false;
						return false;
					}
					flag = true;
					return true;
				}
				catch (Exception ex)
				{
					if (ProviderConfig.m_bTraceLevelNetwork)
					{
						Trace.Write(OracleTraceLevel.Network, OracleTraceTag.Receive, OracleTraceClassName.OracleCommunication, OracleTraceFuncName.TransportAlive, "Got exception during transportAlive:" + ex.ToString());
					}
					flag = false;
					return false;
				}
				finally
				{
					if (ProviderConfig.m_bTraceLevelNetwork)
					{
						Trace.Write(OracleTraceLevel.Network, OracleTraceTag.Receive, OracleTraceClassName.OracleCommunication, OracleTraceFuncName.TransportAlive, "TransportAlive() returns : " + flag);
					}
				}
			}
		}

		internal OracleCommunication(ConOraBufPool oraBufPool)
		{
			m_oraBufPool = oraBufPool;
		}

		internal OracleCommunication(OracleCommunication lsnrEP, ConOraBufPool oraBufPool)
		{
			m_connOption = lsnrEP.m_connOption;
			if (m_connOption == null)
			{
				throw new NetworkException(-6002);
			}
			m_sessionCtx = new SessionContext(m_connOption.SessionDataUnitSize, m_connOption.TransportDataUnitSize);
			m_sessionCtx.m_transportAdapter = lsnrEP.m_sessionCtx.m_transportAdapter.Answer(m_connOption);
			m_sessionCtx.m_socketStream = m_sessionCtx.m_transportAdapter.GetStream();
			m_sessionCtx.isNTConnected = true;
			m_oraBufPool = oraBufPool;
		}

		internal static void GetSEPSUserIDandPW(string ConnectString, out string U, out SecureString PW, out string WP, out string WF)
		{
			SEPS.GetSEPSUandP(ConnectString, out U, out PW, out WP, out WF);
		}

		internal void GetInbandNotification(out int InbandEMFI, out int InbandErr1, out int InbandErr2, out string InbandErrMsg)
		{
			InbandErrMsg = null;
			if (m_sessionCtx != null && m_sessionCtx.m_gotInBandNotif)
			{
				InbandEMFI = m_sessionCtx.m_InBandNotifEMFI;
				InbandErr1 = m_sessionCtx.m_InBandNotifErr1;
				InbandErr2 = m_sessionCtx.m_InBandNotifErr2;
			}
			else
			{
				InbandEMFI = -1;
				InbandErr1 = -1;
				InbandErr2 = -1;
			}
		}

		internal bool InBreakResetMode()
		{
			return m_sessionCtx.m_onBreakReset;
		}

		private void InitConOption(ConnectionOption connOption)
		{
			if (connOption == null)
			{
				throw new NetworkException(-6001);
			}
			m_connOption = connOption;
			connOption.AsyncBufferPool = m_oraBufPool;
			connOption.AsyncBufferInitArg = this;
			connOption.SNOConfig = SNOConfig;
		}

		private void ConnectViaCO(ConnectionOption connOption, AddressResolution addrRes)
		{
			Exception ex = null;
			bool flag = false;
			m_sessionCtx = new SessionContext(m_connOption.SessionDataUnitSize, m_connOption.TransportDataUnitSize);
			m_sessionCtx.m_InbandNotification = m_inbandNotification;
			m_sessionCtx.m_inbandErrContext = m_InbandErrContext;
			m_sessionCtx.m_connectData = (addrRes.useAddrResConnectString ? addrRes.m_tnsAddress : m_connOption.ConnectData);
			m_sessionCtx.m_conops = connOption;
			m_sessionCtx.m_transportAdapter = GetTransportAdapter(m_connOption.Protocol);
			m_sessionCtx.m_transportAdapter.Connect(m_connOption);
			while (!flag)
			{
				try
				{
					Stream socketStream;
					if (m_sessionCtx == null || m_sessionCtx.m_transportAdapter == null || (socketStream = m_sessionCtx.m_transportAdapter.ConnectIterate()) == null)
					{
						break;
					}
					ex = null;
					m_sessionCtx.m_socket = m_sessionCtx.m_transportAdapter.GetSocket();
					m_sessionCtx.m_testSocket = m_sessionCtx.m_transportAdapter.GetTestSocket();
					m_sessionCtx.m_socketStream = socketStream;
					m_sessionCtx.m_readerStream = new ReaderStream(m_sessionCtx, m_connOption);
					m_sessionCtx.m_writerStream = new WriterStream(m_sessionCtx);
					m_sessionCtx.isNTConnected = true;
					if (m_NAHandshake && !m_RAW)
					{
						try
						{
							m_anoObject = new Ano();
							m_anoObject.Initialize(m_sessionCtx);
							m_sessionCtx.m_bAnoEnabled = true;
						}
						catch (Exception)
						{
							m_sessionCtx.m_bAnoEnabled = false;
						}
					}
					if (!m_RAW)
					{
						SendConnectPacketAndProcessResponse(addrRes);
					}
					flag = true;
					continue;
				}
				catch (Exception ex3)
				{
					ex = ex3;
					continue;
				}
			}
			if (ex != null)
			{
				Disconnect();
				throw ex;
			}
		}

		internal void Connect(string tnsDescriptor, ConnectionOption CO = null)
		{
			m_NAHandshake = true;
			m_connOption = CO;
			DoConnect(tnsDescriptor);
		}

		internal void Connect(string tnsDescriptor, bool doNAHandshake, ConnectionOption CO = null)
		{
			m_NAHandshake = doNAHandshake;
			m_connOption = CO;
			DoConnect(tnsDescriptor);
		}

		internal void Connect(string tnsDescriptor, bool doNAHandshake = false, string IName = null, ConnectionOption CO = null)
		{
			m_NAHandshake = doNAHandshake;
			m_InstanceName = IName;
			m_connOption = CO;
			DoConnect(tnsDescriptor);
		}

		private void DoConnect(string tnsDescriptor)
		{
			m_cltEP = true;
			Exception ex = null;
			SNOConfig = new SqlNetOraConfig((m_ObCfgHT != null) ? m_ObCfgHT : ConfigBaseClass.m_configParameters);
			AddressResolution addressResolution = new AddressResolution(tnsDescriptor, SNOConfig, m_ObTnsHT, m_ObLdpHT, m_InstanceName, m_connOption, m_ShardingKeyB64, m_SuperShardingKeyB64, m_ServerIs);
			foreach (ConnectionOption item in addressResolution)
			{
				string text = ((SNOConfig.ExpireTime != null) ? SNOConfig.ExpireTime.Trim() : null);
				try
				{
					InitConOption(item);
					if (!string.IsNullOrEmpty(m_ObCfgWL))
					{
						item.SSL_WALLET_DIRECTORY = m_ObCfgWL;
					}
					if (m_KeepAlive)
					{
						item.m_KeepAlive = true;
						item.m_KeepAliveTime = m_KeepAliveTime * 1000;
						item.m_KeepAliveInterval = m_KeepAliveInterval * 1000;
					}
					else if (!string.IsNullOrEmpty(text))
					{
						int num = AddressResolution.ConvExpTimetoMilli(text);
						if (num > 0)
						{
							item.m_KeepAlive = true;
							item.m_KeepAliveTime = num;
							item.m_KeepAliveInterval = 6000;
						}
					}
					ConnectViaCO(item, addressResolution);
					m_sessionCtx.m_transportAdapter.BeginAsyncReceives(null, m_sessionCtx.m_sessionDataUnit);
					m_sessionCtx.m_usingAsyncReceives = true;
					ex = null;
				}
				catch (NetworkException ex2)
				{
					ex = ex2;
					continue;
				}
				catch (Exception inner)
				{
					ex = new NetworkException(-6001, inner);
					continue;
				}
				break;
			}
			if (ex != null)
			{
				throw ex;
			}
		}

		internal string Answer()
		{
			new ConnectPacket(m_sessionCtx, 0).Receive();
			m_sessionCtx.m_bAnoEnabled = false;
			return null;
		}

		internal void Accept(string AcceptData)
		{
			new AcceptPacket(m_sessionCtx, AcceptData).Send();
			m_sessionCtx.m_readerStream = new ReaderStream(m_sessionCtx, m_connOption);
			m_sessionCtx.m_writerStream = new WriterStream(m_sessionCtx);
			m_sessionCtx.m_transportAdapter.BeginAsyncReceives(null, m_sessionCtx.m_sessionDataUnit);
			m_sessionCtx.m_usingAsyncReceives = true;
			m_sessionCtx.m_NSHandshakeComplete = true;
		}

		internal void Listen(string tnsDescriptor, bool inAddr_Any)
		{
			SNOConfig = new SqlNetOraConfig((m_ObCfgHT != null) ? m_ObCfgHT : ConfigBaseClass.m_configParameters);
			AddressResolution addressResolution = new AddressResolution(tnsDescriptor, SNOConfig);
			InitConOption(addressResolution.ResolveConnectionString());
			m_sessionCtx = new SessionContext(m_connOption.SessionDataUnitSize, m_connOption.TransportDataUnitSize);
			m_sessionCtx.m_connectData = m_connOption.ConnectData;
			m_connOption.inAddr_Any = inAddr_Any;
			m_sessionCtx.m_transportAdapter = GetTransportAdapter(m_connOption.Protocol);
			m_sessionCtx.m_transportAdapter.Listen(m_connOption);
			m_sessionCtx.isNTConnected = true;
			m_lsnEP = true;
		}

		internal void Listen(string tnsDescriptor)
		{
			Listen(tnsDescriptor, inAddr_Any: false);
		}

		internal void Disconnect()
		{
			if (m_sessionCtx.isNTConnected)
			{
				m_sessionCtx.m_transportAdapter.Disconnect();
				if (m_sessionCtx.m_socketStream != null)
				{
					m_sessionCtx.m_socketStream.Close();
					m_sessionCtx.m_socketStream.Dispose();
				}
				m_sessionCtx.m_socketStream = null;
				m_sessionCtx.m_transportAdapter = null;
				m_sessionCtx.isNTConnected = false;
			}
		}

		internal string Resolve(string tnsAlias, out ConnectionOption CO)
		{
			SqlNetOraConfig sNOConfig = new SqlNetOraConfig((m_ObCfgHT != null) ? m_ObCfgHT : ConfigBaseClass.m_configParameters);
			AddressResolution addressResolution = new AddressResolution(tnsAlias, sNOConfig, m_ObTnsHT, m_ObLdpHT);
			CO = addressResolution.m_ConnectionOption;
			return addressResolution.m_tnsAddress;
		}

		internal bool IsNAEInUse()
		{
			if (m_sessionCtx == null || m_sessionCtx.m_ano == null)
			{
				return false;
			}
			return m_sessionCtx.cryptoNeeded;
		}

		internal void SendMarker(int markerType)
		{
			if (!m_sessionCtx.isNTConnected)
			{
				throw new NetworkException(12614);
			}
			new MarkerPacket(m_sessionCtx, markerType).Send();
		}

		internal void Break()
		{
			if (!m_sessionCtx.m_onBreakReset)
			{
				m_sessionCtx.m_onBreakReset = true;
				if ((m_sessionCtx.m_negotiatedOptions & TNSPacketOffsets.NSGRECVATTN) > 0)
				{
					m_sessionCtx.m_transportAdapter.SendUrgent(new byte[1]
					{
						33
					}, 0, 1);
				}
				else
				{
					SendMarker(MarkerPacket.NIQBMARK);
				}
			}
		}

		internal void Reset()
		{
			if (!m_sessionCtx.m_onBreakReset)
			{
				throw new NetworkException(-6000);
			}
			m_sessionCtx.m_writerStream.DiscardData();
			SendMarker(MarkerPacket.NIQRMARK);
			while (!m_sessionCtx.m_gotReset)
			{
				if (m_sessionCtx.m_usingAsyncReceives)
				{
					m_sessionCtx.m_readerStream.WaitForReset();
					continue;
				}
				Packet packet = new Packet(m_sessionCtx, m_sessionCtx.m_sessionDataUnit);
				packet.Receive();
				if (packet.m_type == TNSPacketType.MARKER && new MarkerPacket(packet).m_isResetMarker)
				{
					m_sessionCtx.m_gotReset = true;
				}
			}
			m_sessionCtx.m_onBreakReset = (m_sessionCtx.m_gotReset = false);
			if (m_sessionCtx.m_ano != null)
			{
				if (m_sessionCtx.m_ano.dataIntegrityAlg != null)
				{
					m_sessionCtx.m_ano.dataIntegrityAlg.renew();
				}
				if (m_sessionCtx.encryptionAlg != null)
				{
					m_sessionCtx.encryptionAlg.setSessionKey(null, null);
				}
			}
		}

		private void SendConnectPacketAndProcessResponse(AddressResolution addrRes)
		{
			NVPair nVPair = null;
			int errorCode = 0;
			Packet packet = null;
			RefusePacket refusePacket = null;
			RedirectPacket redirectPacket = null;
			bool flag = false;
			ConnectPacket connectPacket = new ConnectPacket(m_sessionCtx, m_connOption.OnRedirect);
			while (!flag)
			{
				connectPacket.Send();
				m_connOption.OnRedirect = false;
				packet = new Packet(m_sessionCtx, m_sessionCtx.m_sessionDataUnit);
				packet.Receive();
				switch (packet.m_type)
				{
				case TNSPacketType.ACCEPT:
					new AcceptPacket(packet);
					m_sessionCtx.m_readerStream = new ReaderStream(m_sessionCtx, m_connOption);
					m_sessionCtx.m_writerStream = new WriterStream(m_sessionCtx);
					m_sessionCtx.m_NSHandshakeComplete = true;
					if (m_NAHandshake && m_sessionCtx.m_ano != null)
					{
						if (((uint)m_sessionCtx.m_ACFL0 & (true ? 1u : 0u)) != 0 && (m_sessionCtx.m_ACFL0 & 4) == 0 && (m_sessionCtx.m_ACFL1 & 8) == 0)
						{
							m_sessionCtx.m_ano.StartNegotiation();
						}
						else
						{
							m_sessionCtx.m_bAnoEnabled = false;
							m_sessionCtx.m_ano = null;
						}
					}
					flag = true;
					if (ProviderConfig.m_bTraceLevelNetwork)
					{
						Trace.Write(OracleTraceLevel.Network, OracleTraceTag.None, OracleTraceClassName.OracleCommunication, OracleTraceFuncName.SendConnectPacketAndProcessResponse, "NS Handshake completed successfully");
						Trace.Write(OracleTraceLevel.Network, OracleTraceTag.None, OracleTraceClassName.OracleCommunication, OracleTraceFuncName.SendConnectPacketAndProcessResponse, "Negotiated SDU size = " + m_sessionCtx.m_sessionDataUnit);
					}
					break;
				case TNSPacketType.REFUSE:
					refusePacket = new RefusePacket(packet);
					if (m_sessionCtx != null && m_sessionCtx.m_socket != null)
					{
						m_sessionCtx.m_socket.Close();
					}
					try
					{
						if (refusePacket.Data == null)
						{
							errorCode = 12564;
						}
						else
						{
							nVPair = NVNavigator.FindNVPairRecurse(NVFactory.CreateNVPair(refusePacket.Data), "ERROR");
							NVPair nVPair2 = ((nVPair == null) ? NVNavigator.FindNVPairRecurse(NVFactory.CreateNVPair(refusePacket.Data), "ERR") : NVNavigator.FindNVPairRecurse(nVPair, "CODE"));
							if (nVPair2 != null)
							{
								errorCode = int.Parse(nVPair2.ValueToString());
							}
						}
					}
					catch (Exception)
					{
						errorCode = 12564;
					}
					throw new NetworkException(errorCode);
				case TNSPacketType.REDIRECT:
					redirectPacket = new RedirectPacket(packet);
					Disconnect();
					addrRes.BuildCO_Redirect(redirectPacket.redirectAddress, ref m_connOption);
					if (redirectPacket.redirectConnectData != null)
					{
						m_connOption.ConnectData = redirectPacket.redirectConnectData;
					}
					m_connOption.OnRedirect = true;
					ConnectViaCO(m_connOption, addrRes);
					flag = true;
					break;
				case TNSPacketType.RESEND:
					if ((packet.m_flags & Packet.NSPFSRN) != 0 && m_sessionCtx.m_transportAdapter.NeedReneg)
					{
						m_sessionCtx.m_transportAdapter.Renegotiate(m_connOption);
						m_sessionCtx.m_socketStream = m_sessionCtx.m_transportAdapter.GetStream();
						m_sessionCtx.m_socket = m_sessionCtx.m_transportAdapter.GetSocket();
						if (m_sessionCtx.m_socketStream == null)
						{
							throw new NetworkException(12614);
						}
					}
					break;
				default:
					m_sessionCtx.m_transportAdapter.Disconnect();
					throw new NetworkException(12566);
				}
			}
		}

		internal void SetFoldInKey(byte[] Key)
		{
			Ano ano;
			byte[] skey;
			if (Key != null && m_sessionCtx != null && (ano = m_sessionCtx.m_ano) != null && m_sessionCtx.cryptoNeeded && (skey = ano.skey) != null)
			{
				int num = Math.Min(Key.Length, skey.Length);
				while (num-- != 0)
				{
					skey[num] ^= Key[num];
				}
				if (m_sessionCtx.encryptionAlg != null)
				{
					m_sessionCtx.encryptionAlg.setSessionKey(skey, ano.getInitializationVector());
					ano.foldedinkey = true;
				}
				if (m_sessionCtx.m_ano.dataIntegrityAlg != null)
				{
					m_sessionCtx.m_ano.dataIntegrityAlg.takeSessionKey(skey, ano.getInitializationVector());
					ano.foldedinkey = true;
				}
			}
		}

		internal ITransportAdapter GetTransportAdapter(string protocol)
		{
			if (string.Equals(protocol, "TCPS", StringComparison.InvariantCultureIgnoreCase))
			{
				return new TcpsTransportAdapter(null);
			}
			if (string.Equals(protocol, "TCP", StringComparison.InvariantCultureIgnoreCase))
			{
				return new TcpTransportAdapter(null);
			}
			if (string.Equals(protocol, "WSS", StringComparison.InvariantCultureIgnoreCase) || string.Equals(protocol, "WS", StringComparison.InvariantCultureIgnoreCase))
			{
				return new WssTransportAdapter(null);
			}
			throw new NetworkException(12538);
		}
	}
}
